react 现在是多少了,与之前有什么区别?

核心考点

  • fiber
  • 事件系统
  • 渲染

概念

React 绑定 this

把一个对象的方法赋值给一个变量会造成 this 的丢失,所以需要绑定 this,把绑定放在构造函数中可以保证只绑定一次函数。

其他的解决方式:

  1. 使用箭头函数。
  2. 构造函数中主动绑定 this。
  3. render 函数中使用箭头函数。但是 注意: 组件每次渲染时,在 render 方法中的箭头函数都会创建一个新的函数,这可能会影响性能。

实现这一点有三种可能的方法:

  1. Binding in Constructor: 在 JavaScript 类中,方法默认不被绑定。这也适用于定义为类方法的 React 事件处理程序。通常我们在构造函数中绑定它们。
class Component extends React.Componenet {
  constructor(props) {
    super(props);
    this.handleClick = this.handleClick.bind(this);
  }

  handleClick() {
    // ...
  }
}

Public class fields syntax: 如果你不喜欢 bind 方案,则可以使用 public class fields syntax 正确绑定回调。

handleClick = () => {
  console.log("this is:", this);
};
<button onClick={this.handleClick}>{"Click me"}</button>

Arrow functions in callbacks: 你可以在回调函数中直接使用 arrow functions

<button onClick={(event) => this.handleClick(event)}>{"Click me"}</button>

注意: 如果回调函数作为属性传给子组件,那么这些组件可能触发一个额外的重新渲染。在这些情况下,考虑到性能,最好使用 .bind()public class fields syntax 方案。

为什么有组件名称要首字母大写?

  1. 因为组件不是 DOM 元素,它们是构造函数。
  2. 小写标记名称是指 HTML 元素,而不是组件。

setState 是同步还是异步,立即获取值的操作

setState 实现机制

合并操作

调用 component 的 setState 方法的时候, React 将其标记为 dirty. 到每一个事件循环结束, React 检查所有标记 dirty 的 component 重新绘制

概念

React 不是 MV* 框架,是一个用于构建用户界面的 JavaScript 库,侧重于 View 层。

React 支持所有流行的浏览器,包括 Internet Explorer 9 和更高版本,但旧版本的浏览器(如 IE 9 和 IE 10)需要一些 polyfill。如果你使用 es5-shim and es5-sham polyfill,那么它甚至支持不支持 ES5 方法的旧浏览器。

React 的优点:

  1. React 只关注 View 层,易于与框架(Angular,Backbone)集成,因为它只是一个视图库。
  2. 虚拟 DOM + diff 算法 -> 不直接操作 DOM 对象
  3. Components 组件,使用 Virtual DOM 提高应用程序的性能。
  4. JSX 的引入,使得组件的代码更加可读,也更容易看懂组件的布局,或者组件之间是如何互相引用的。
  5. State 触发视图的渲染 -> 单向数据绑定
  6. 支持服务端渲染,可改进 SEO 和性能。

事件机制

const Test = ({ list, handleClick }) =>
  list.map((item, index) => (
    <span onClick={handleClick} key={index}>
      {index}
    </span>
  ));

React 其实自己实现了一套事件机制:

以上类似代码想必大家经常会写到,但是你是否考虑过点击事件是否绑定在了每一个标签上?事实当然不是,JSX 上写的事件并没有绑定在对应的真实 DOM 上,而是通过事件代理的方式,将所有的事件都统一绑定在了 document 上。这样的方式不仅减少了内存消耗,还能在组件挂载销毁时统一订阅和移除事件。

另外冒泡到 document 上的事件也不是原生浏览器事件,而是 React 自己实现的合成事件(SyntheticEvent)。因此我们如果不想要事件冒泡的话,调用 event.stopPropagation 是无效的,而应该调用 event.preventDefault

SyntheticEvent 是对浏览器原生事件的跨浏览器包装。它的 API 与浏览器的原生事件相同,包括 stopPropagation()preventDefault(),除了事件在所有浏览器中的工作方式相同。

那么实现合成事件的目的是什么呢?分别是:

  1. 合成事件首先抹平了浏览器之间的兼容问题,屏蔽了底层浏览器的细节差异,保证了行为的一致性。另外这是一个跨浏览器原生事件包装器,赋予了跨浏览器开发的能力
  2. 对于原生浏览器事件来说,浏览器会给监听器创建一个事件对象。如果你有很多的事件监听,那么就需要分配很多的事件对象,造成高额的内存分配问题。但是对于合成事件来说,有一个事件池专门来管理它们的创建和销毁,当事件需要被使用时,就会从池子中复用对象,事件回调结束后,就会销毁事件对象上的属性,从而便于下次复用事件对象。

特点:

  • 使用事件委托技术进行事件代理,React 组件上声明的事件最终都转化为 DOM 原生事件,绑定到了 document 这个 DOM 节点上。从而减少了内存开销。
  • 自身实现了一套事件冒泡机制。所有事件绑定在 document 上
  • 所以事件触发的都是 ReactEventListener 的 dispatch 方法

HTML 和 React 事件处理有什么区别?

在 HTML 中事件名必须小写:

<button onclick="activateLasers()"></button>

而在 React 中它遵循 camelCase (驼峰) 惯例:

<button onClick={activateLasers}>

在 HTML 中你可以返回 false 以阻止默认的行为:

<a href="#" onclick='console.log("The link was clicked."); return false;' />

而在 React 中你必须地明确地调用 preventDefault()

function handleClick(event) {
  event.preventDefault();
  console.log("The link was clicked.");
}

React 中是怎么实现事件代理的?

React 并不会真正的绑定事件到每一个具体的元素上,而是采用事件代理的模式:在根节点 document 上为每种事件添加唯一的 Listener,然后通过事件的 target 找到真实的触发元素。这样从触发元素到顶层节点之间的所有节点如果有绑定这个事件,React 都会触发对应的事件处理函数。这就是所谓的 React 模拟事件系统。

尽管整个事件系统由 React 管理,但是其 API 和使用方法与原生事件一致。这种机制确保了跨浏览器的一致性:在所有浏览器(IE8 及以上)都可以使用符合 W3C 标准的 API,包括 stopPropagation(),preventDefault()等等。对于事件的冒泡(bubble)和捕获(capture)模式也都完全支持。

Component

当渲染一个列表时,何为 key?设置 key 的目的是什么

key 是 React 中用于追踪哪些列表中元素被修改、删除或者被添加的辅助标识。在 diff 算法中,key 用来判断该元素节点是被移动过来的还是新创建的元素,减少不必要的元素重复渲染。

(在构造函数中)调用 super(props) 的目的是什么

super() 被调用之前,子类是不能使用 this 的,在 ES2015 中,子类必须在 constructor 中调用 super()。传递 propssuper() 的原因则是便于(在子类中)能在 constructor 访问 this.props

shouldComponentUpdate(nextProps, nextState)

当父组件被重新渲染时即 render 函数执行时,子组件就会默认被重新渲染,但很多时候是不需要重新渲染每一个子组件的。这时就可以使用 shouldComponentUpdate 来判断是否真的需要重新渲染子组件。仅仅一个判断,就可以节约很多的消耗。 所以对于父组件发生变化而子组件不变的情况,使用 shouldComponentUpdate 会提升性能。

使用 PureComponent

PureComponent内部帮我们实现了shouldComponentUpdate的比较,其他和 Component 一样。但是在 shouldComponentUpdate 进行的是一个浅比较,看看官方文档是怎么说的。

浅比较只比较第一层的基本类型和引用类型值是否相同

如果数据结构比较复杂,那么可能会导致一些问题,要么当你知道改变的时候调用forceUpdate,要么使用immutable来包装你的 state

类组件(Class component)和函数式组件(Functional component)之间有何不同

  • 类组件不仅允许你使用更多额外的功能,如组件自身的状态和生命周期钩子,也能使组件直接访问 store 并维持状态
  • 当组件仅是接收 props,并将组件自身渲染到页面时,该组件就是一个 '无状态组件(stateless component)',可以使用一个纯函数来创建这样的组件。这种组件也被称为哑组件(dumb components)或展示组件

通信

其实 React 中的组件通信基本和 Vue 中的一致。同样也分为以下三种情况:

  • 父子组件通信
  • 兄弟组件通信
  • 跨多层级组件通信
  • 任意组件

父子通信

父组件通过 props 传递数据给子组件,子组件通过调用父组件传来的函数传递数据给父组件,这两种方式是最常用的父子通信实现办法。

这种父子通信方式也就是典型的单向数据流,父组件通过 props 传递数据,子组件不能直接修改 props, 而是必须通过调用父组件函数的方式告知父组件修改数据。

兄弟组件通信

对于这种情况可以通过共同的父组件来管理状态和事件函数。比如说其中一个兄弟组件调用父组件传递过来的事件函数修改父组件中的状态,然后父组件将状态传递给另一个兄弟组件。

跨多层次组件通信

如果你使用 16.3 以上版本的话,对于这种情况可以使用 Context API。

// 创建 Context,可以在开始就传入值
const StateContext = React.createContext()
class Parent extends React.Component {
  render () {
return (
  // value 就是传入 Context 中的值
  <StateContext.Provider value='yiliang114'>
<Child />
  </StateContext.Provider>
)
  }
}
class Child extends React.Component {
  render () {
return (
  <ThemeContext.Consumer>
// 取出值
{context => (
  name is { context }
)}
  </ThemeContext.Consumer>
);
  }
}

任意组件

这种方式可以通过 Redux 或者 Event Bus 解决,另外如果你不怕麻烦的话,可以使用这种方式解决上述所有的通信情况

HOC 是什么?相比 mixins 有什么优点?

很多人看到高阶组件(HOC)这个概念就被吓到了,认为这东西很难,其实这东西概念真的很简单,我们先来看一个例子。

function add(a, b) {
return a + b
}

现在如果我想给这个 add 函数添加一个输出结果的功能,那么你可能会考虑我直接使用 console.log 不就实现了么。说的没错,但是如果我们想做的更加优雅并且容易复用和扩展,我们可以这样去做:

function withLog (fn) {
function wrapper(a, b) {
const result = fn(a, b)
console.log(result)
return result
}
return wrapper
}
const withLogAdd = withLog(add)
withLogAdd(1, 2)

其实这个做法在函数式编程里称之为高阶函数,大家都知道 React 的思想中是存在函数式编程的,高阶组件和高阶函数就是同一个东西。我们实现一个函数,传入一个组件,然后在函数内部再实现一个函数去扩展传入的组件,最后返回一个新的组件,这就是高阶组件的概念,作用就是为了更好的复用代码。

其实 HOC 和 Vue 中的 mixins 作用是一致的,并且在早期 React 也是使用 mixins 的方式。但是在使用 class 的方式创建组件以后,mixins 的方式就不能使用了,并且其实 mixins 也是存在一些问题的,比如:

  • 隐含了一些依赖,比如我在组件中写了某个 state 并且在 mixin 中使用了,就这存在了一个依赖关系。万一下次别人要移除它,就得去 mixin 中查找依赖
  • 多个 mixin 中可能存在相同命名的函数,同时代码组件中也不能出现相同命名的函数,否则就是重写了,其实我一直觉得命名真的是一件麻烦事。。
  • 雪球效应,虽然我一个组件还是使用着同一个 mixin,但是一个 mixin 会被多个组件使用,可能会存在需求使得 mixin 修改原本的函数或者新增更多的函数,这样可能就会产生一个维护成本

HOC 解决了这些问题,并且它们达成的效果也是一致的,同时也更加的政治正确(毕竟更加函数式了)。

何时使用 Component 还是 PureComponent?

PureComponent 通过 prop 和 state 的浅比较来实现 shouldComponentUpdate,某些情况下可以用 PureComponent 提升性能

所谓浅比较(shallowEqual),即 react 源码中的一个函数,然后根据下面的方法进行是不是 PureComponent 的判断,帮我们做了本来应该我们在 shouldComponentUpdate 中做的事情

if (this._compositeType === CompositeTypes.PureClass) {
  shouldUpdate =
    !shallowEqual(prevProps, nextProps) || !shallowEqual(inst.state, nextState);
}
shouldComponentUpdate(nextProps, nextState) {
return (nextState.person !== this.state.person);
}

什么时候不该用?

PureComponent 中的判断逻辑是浅比较,如果当状态更新时是一个引用对象内部的更新,那么这个时候是不使用的

组件的异步数据加载

我不想在一开始挂载组件的时候就去异步操作(或者可以直接懒加载,那就可以在 constructor 中直接去加载异步数据。。),比如说 modal 的异步数据加载,最好是在 visable === true 的时候进行 ajax,否则 ajax 消耗的事件影响首屏体验。

react hoc

HOC(全称 Higher-order component)是一种 React 的进阶使用方法,主要还是为了便于组件的复用。HOC 就是一个方法,获取一个组件,返回一个更高级的组件。

https://segmentfault.com/a/1190000008112017?_ea=1553893

如何在 React 中创建组件?

有两种可行的方法来创建一个组件:

Function Components: 这是创建组件最简单的方式。这些是纯 JavaScript 函数,接受 props 对象作为第一个参数并返回 React 元素:

function Greeting({ message }) {
  return <h1>{`Hello, ${message}`}</h1>;
}

Class Components: 你还可以使用 ES6 类来定义组件。上面的函数组件若使用 ES6 的类可改写为:

class Greeting extends React.Component {
  render() {
    return <h1>{`Hello, ${this.props.message}`}</h1>;
  }
}

通过以上任意方式创建的组件,可以这样使用:

<Greeting message="semlinker" />

在 React 内部对函数组件和类组件的处理方式是不一样的,如:

// 如果 Greeting 是一个函数
const result = Greeting(props); // <p>Hello</p>

// 如果 Greeting 是一个类
const instance = new Greeting(props); // Greeting {}
const result = instance.render(); // <p>Hello</p>

何时使用类组件和函数组件?

如果组件需要使用状态或生命周期方法,那么使用类组件,否则使用函数组件。

什么是 Pure Components?

React.PureComponentReact.Component 完全相同,只是它为你处理了 shouldComponentUpdate() 方法。当属性或状态发生变化时,PureComponent 将对属性和状态进行浅比较。另一方面,一般的组件不会将当前的属性和状态与新的属性和状态进行比较。因此,在默认情况下,每当调用 shouldComponentUpdate 时,默认返回 true,所以组件都将重新渲染。

什么是受控组件?

在随后的用户输入中,能够控制表单中输入元素的组件被称为受控组件,即每个状态更改都有一个相关联的处理程序。

例如,我们使用下面的 handleChange 函数将输入框的值转换成大写:

handleChange(event) {
  this.setState({value: event.target.value.toUpperCase()})
}

什么是非受控组件?

非受控组件是在内部存储其自身状态的组件,当需要时,可以使用 ref 查询 DOM 并查找其当前值。这有点像传统的 HTML。

在下面的 UserProfile 组件中,我们通过 ref 引用 name 输入框:

class UserProfile extends React.Component {
  constructor(props) {
    super(props);
    this.handleSubmit = this.handleSubmit.bind(this);
    this.input = React.createRef();
  }

  handleSubmit(event) {
    alert("A name was submitted: " + this.input.current.value);
    event.preventDefault();
  }

  render() {
    return (
      <form onSubmit={this.handleSubmit}>
        <label>
          {"Name:"}
          <input type="text" ref={this.input} />
        </label>
        <input type="submit" value="Submit" />
      </form>
    );
  }
}

在大多数情况下,建议使用受控组件来实现表单。

什么是无状态组件?

如果行为独立于其状态,则它可以是无状态组件。你可以使用函数或类来创建无状态组件。但除非你需要在组件中使用生命周期钩子,否则你应该选择函数组件。无状态组件有很多好处: 它们易于编写,理解和测试,速度更快,而且你可以完全避免使用this关键字。

什么是有状态组件?

如果组件的行为依赖于组件的state,那么它可以被称为有状态组件。这些有状态组件总是类组件,并且具有在constructor中初始化的状态。

class App extends Component {
  constructor(props) {
    super(props);
    this.state = { count: 0 };
  }

  render() {
    // ...
  }
}

createElement() 和 cloneElement() 方法有什么区别?

JSX 元素将被转换为 React.createElement() 函数来创建 React 元素,这些对象将用于表示 UI 对象。而 cloneElement 用于克隆元素并传递新的属性。

推荐的组件命名方法是什么?

建议通过引用命名组件,而不是使用 displayName

使用 displayName 命名组件:

export default React.createClass({
  displayName: "TodoApp",
  // ...
});

推荐的方式:

export default class TodoApp extends React.Component {
  // ...
}

在组件类中方法的推荐顺序是什么?

mountingrender stage 阶段推荐的方法顺序:

static 方法 constructor()getChildContext()componentWillMount()componentDidMount()componentWillReceiveProps()shouldComponentUpdate()componentWillUpdate()componentDidUpdate()componentWillUnmount() 点击处理程序或事件处理程序,如 onClickSubmit()onChangeDescription() 用于渲染的 getter 方法,如 getSelectReason()getFooterContent() 可选的渲染方法,如 renderNavigation()renderProfilePicture()render()

什么是 switching 组件?

switching 组件是渲染多个组件之一的组件。我们需要使用对象将 prop 映射到组件中。

例如,以下的 switching 组件将基于 page 属性显示不同的页面:

import HomePage from "./HomePage";
import AboutPage from "./AboutPage";
import ServicesPage from "./ServicesPage";
import ContactPage from "./ContactPage";

const PAGES = {
  home: HomePage,
  about: AboutPage,
  services: ServicesPage,
  contact: ContactPage,
};

const Page = (props) => {
  const Handler = PAGES[props.page] || ContactPage;

  return <Handler {...props} />;
};

// The keys of the PAGES object can be used in the prop types to catch dev-time errors.
Page.propTypes = {
  page: PropTypes.oneOf(Object.keys(PAGES)).isRequired,
};

受控组件(controlled component)

一个输入表单元素,它的值通过 React 的这种方式来控制,这样的元素就被称为"受控元素"。

在 HTML 中,类似 <input>, <textarea><select> 这样的表单元素会维护自身的状态,并基于用户的输入来更新。但在 React 中会有些不同,包含表单元素的组件将会在 state 中追踪输入的值。

高阶组件 HOC (higher order component)

高阶组件是一个以组件为参数并返回一个新组件的函数。

HOC 允许你重用代码、逻辑和引导抽象。最常见的可能是 Redux 的 connect 函数。除了简单分享工具库和简单的组合,HOC 最好的方式是共享 React 组件之间的行为。如果你发现你在不同的地方写了大量代码来做同一件事时,就应该考虑将代码重构为可重用的 HOC。

function add(a, b) {
  return a + b;
}

现在如果我想给这个 add 函数添加一个输出结果的功能,那么你可能会考虑我直接使用 console.log 不就实现了么。说的没错,但是如果我们想做的更加优雅并且容易复用和扩展,我们可以这样去做:

function withLog(fn) {
  function wrapper(a, b) {
    const result = fn(a, b);
    console.log(result);
    return result;
  }
  return wrapper;
}
const withLogAdd = withLog(add);
withLogAdd(1, 2);

这个做法在函数式编程里称之为高阶函数,大家都知道 React 的思想中是存在函数式编程的,高阶组件和高阶函数就是同一个东西。我们实现一个函数,传入一个组件,然后在函数内部再实现一个函数去扩展传入的组件,最后返回一个新的组件,这就是高阶组件的概念,作用就是为了更好的复用代码。

createElement 与 cloneElement 的区别是什么

createElement 函数是 JSX 编译之后使用的创建 React Element 的函数,而 cloneElement 则是用于复制某个元素并传入新的 Props

react 组件的划分业务组件技术组件?

  • 根据组件的职责通常把组件分为 UI 组件和容器组件。
  • UI 组件负责 UI 的呈现,容器组件负责管理数据和逻辑。
  • 两者通过React-Redux 提供connect方法联系起来

react 的渲染过程中,兄弟节点之间是怎么处理的?也就是 key 值不一样的时候。

通常我们输出节点的时候都是 map 一个数组然后返回一个 ReactNode,为了方便 react 内部进行优化,我们必须给每一个 reactNode 添加 key,这个 key prop 在设计值处不是给开发者用的,而是给 react 用的,大概的作用就是给每一个 reactNode 添加一个身份标识,方便 react 进行识别,在重渲染过程中,如果 key 一样,若组件属性有所变化,则 react 只更新组件对应的属性;没有变化则不更新,如果 key 不一样,则 react 先销毁该组件,然后重新创建该组件。

react 拖拽组件实现

https://www.zhihu.com/question/60339629

React 父组件调用子组件的方法

https://blog.csdn.net/baidu_38151187/article/details/80582416

React 非父子组件函数调用? 以及传值方式

portals 的典型使用场景是什么?

当父组件拥有 overflow: hidden 或含有影响堆叠上下文的属性(z-index、position、opacity 等样式),且需要脱离它的容器进行展示时,React portal 就非常有用。例如,对话框、全局消息通知、悬停卡和工具提示。

如何设置非受控组件的默认值?

在 React 中,表单元素的属性值将覆盖其 DOM 中的值。对于非受控组件,你可能希望能够指定其初始值,但不会控制后续的更新。要处理这种情形,你可以指定一个 defaultValue 属性来取代 value 属性。

render() {
  return (
<form onSubmit={this.handleSubmit}>
  <label>
User Name:
<input
  defaultValue="John"
  type="text"
  ref={this.input} />
  </label>
  <input type="submit" value="Submit" />
</form>
  );
}

这同样适用于 selecttextArea 输入框。但对于 checkboxradio 控件,需要使用 defaultChecked

在 Pure Component 中使用渲染属性会有什么问题?

如果在渲染方法中创建函数,则会否定纯组件的用途。因为浅属性比较对于新属性总是返回 false,在这种情况下,每次渲染都将为渲染属性生成一个新值。你可以通过将渲染函数定义为实例方法来解决这个问题。

如何使用渲染属性创建 HOC?

可以使用带有渲染属性的常规组件实现大多数高阶组件(HOC)。例如,如果希望使用 withMouse HOC 而不是 <Mouse> 组件,则你可以使用带有渲染属性的常规 <Mouse> 组件轻松创建一个 HOC 组件。

function withMouse(Component) {
  return class extends React.Component {
    render() {
      return (
        <Mouse
          render={(mouse) => <Component {...this.props} mouse={mouse} />}
        />
      );
    }
  };
}

如何有条件地渲染组件?

在某些情况下,你希望根据某些状态渲染不同的组件。 JSX 不会渲染 falseundefined,因此你可以使用 && 运算符,在某个条件为 true 时,渲染组件中指定的内容。

const MyComponent = ({ name, address }) => (
  <div>
    <h2>{name}</h2>
    {address && <p>{address}</p>}
  </div>
);

如果你需要一个 if-else 条件,那么使用三元运算符:

const MyComponent = ({ name, address }) => (
  <div>
    <h2>{name}</h2>
    {address ? <p>{address}</p> : <p>{"Address is not available"}</p>}
  </div>
);

如何 memoize(记忆)组件?

有可用于函数组件的 memoize 库。例如 moize 库可以将组件存储在另一个组件中。

import moize from "moize";
import Component from "./components/Component"; // this module exports a non-memoized component

const MemoizedFoo = moize.react(Component);

const Consumer = () => {
  <div>
    {"I will memoize the following entry:"}
    <MemoizedFoo />
  </div>;
};

React Mixins 是什么?

Mixins* 是一种完全分离组件通用功能的方法。 Mixins 不应该被继续使用,可以用高阶组件或装饰器来替换。

最常用的 mixins 是 PureRenderMixin。当 props 和状态与之前的 props 和状态相等时,你可能在某些组件中使用它来防止不必要的重新渲染:

const PureRenderMixin = require("react-addons-pure-render-mixin");
const Button = React.createClass({
  mixins: [PureRenderMixin],
  // ...
});

为什么组件名称应该以大写字母开头?

如果使用 JSX 渲染组件,则该组件的名称必须以大写字母开头,否则 React 将会抛出无法识别标签的错误。这种约定是因为只有 HTML 元素和 SVG 标签可以以小写字母开头。

定义组件类的时候,你可以以小写字母开头,但在导入时应该使用大写字母。

class myComponent extends Component {
  render() {
    return <div />;
  }
}

export default myComponent;

当在另一个文件导入时,应该以大写字母开头:

import MyComponent from "./MyComponent";

为什么不需要使用继承?

在 React 中,建议使用组合而不是继承来重用组件之间的代码。Props 和 composition 都为你提供了以一种明确和安全的方式自定义组件外观和行为所需的灵活性。但是,如果你希望在组件之间复用非 UI 功能,建议将其提取到单独的 JavaScript 模块中。之后的组件导入它并使用该函数、对象或类,而不需扩展它。

我可以在 React 应用程序中可以使用 web components 么?

是的,你可以在 React 应用程序中使用 Web Components。尽管许多开发人员不会使用这种组合方式,但如果你使用的是使用 Web Components 编写的第三方 UI 组件,则可能需要这种组合。例如,让我们使用 Vaadin 提供的 Web Components 日期选择器组件:

import React, { Component } from "react";
import "./App.css";
import "@vaadin/vaadin-date-picker";

class App extends Component {
  render() {
    return (
      <div className="App">
        <vaadin-date-picker label="When were you born?"></vaadin-date-picker>
      </div>
    );
  }
}

export default App;

什么是动态导入?

动态导入语法是 ECMAScript 提案,目前不属于语言标准的一部分。它有望在不久的将来被采纳。在你的应用程序中,你可以使用动态导入来实现代码拆分。让我们举一个加法的例子:

Normal Import

import { add } from "./math";
console.log(add(10, 20));

Dynamic Import

import("./math").then((math) => {
  console.log(math.add(10, 20));
});

什么是 loadable 组件?

如果你想要在服务端渲染的应用程序中实现代码拆分,建议使用 Loadable 组件,因为 React.lazy 和 Suspense 还不可用于服务器端渲染。Loadable 允许你将动态导入的组件作为常规的组件进行渲染。让我们举一个例子:

import loadable from "@loadable/component";

const OtherComponent = loadable(() => import("./OtherComponent"));

function MyComponent() {
  return (
    <div>
      <OtherComponent />
    </div>
  );
}

现在,其他组件将以单独的包进行加载。

什么是 suspense 组件?

如果父组件在渲染时包含 dynamic import 的模块尚未加载完成,在此加载过程中,你必须使用一个 loading 指示器显示后备内容。这可以使用 Suspense 组件来实现。例如,下面的代码使用 Suspense 组件:

const OtherComponent = React.lazy(() => import("./OtherComponent"));

function MyComponent() {
  return (
    <div>
      <Suspense fallback={<div>Loading...</div>}>
        <OtherComponent />
      </Suspense>
    </div>
  );
}

正如上面的代码中所展示的,懒加载的组件被包装在 Suspense 组件中。

什么是基于路由的代码拆分?

进行代码拆分的最佳位置之一是路由。整个页面将立即重新渲染,因此用户不太可能同时与页面中的其他元素进行交互。因此,用户体验不会受到干扰。让我们以基于路由的网站为例,使用像 React Router 和 React.lazy 这样的库:

import { BrowserRouter as Router, Route, Switch } from "react-router-dom";
import React, { Suspense, lazy } from "react";

const Home = lazy(() => import("./routes/Home"));
const About = lazy(() => import("./routes/About"));

const App = () => (
  <Router>
    <Suspense fallback={<div>Loading...</div>}>
      <Switch>
        <Route exact path="/" component={Home} />
        <Route path="/about" component={About} />
      </Switch>
    </Suspense>
  </Router>
);

在上面的代码中,代码拆分将发生在每个路由层级。

什么是 Keyed Fragments ?

使用显式 <React.Fragment> 语法声明的片段可能具有 key 。一般用例是将集合映射到片段数组,如下所示,

function Glossary(props) {
  return (
    <dl>
      {props.items.map((item) => (
        // Without the `key`, React will fire a key warning
        <React.Fragment key={item.id}>
          <dt>{item.term}</dt>
          <dd>{item.description}</dd>
        </React.Fragment>
      ))}
    </dl>
  );
}

注意: 键是唯一可以传递给 Fragment 的属性。将来,可能会支持其他属性,例如事件处理程序。

如何每秒更新一个组件?

你需要使用 setInterval() 来触发更改,但也需要在组件卸载时清除计时器,以防止错误和内存泄漏。

componentDidMount() {
  this.interval = setInterval(() => this.setState({ time: Date.now() }), 1000)
}

componentWillUnmount() {
  clearInterval(this.interval)
}

如何使用 React 和 ES6 导入和导出组件?

导出组件时,你应该使用默认导出:

import React from "react";
import User from "user";

export default class MyProfile extends React.Component {
  render() {
    return <User type="customer">//...</User>;
  }
}

使用 export 说明符,MyProfile 将成为成员并导出到此模块,此外在其他组件中你无需指定名称就可以导入相同的内容。

为什么 React 组件名称必须以大写字母开头?

在 JSX 中,小写标签被认为是 HTML 标签。但是,含有 . 的大写和小写标签名却不是。

<component /> 将被转换为 React.createElement('component') (i.e, HTML 标签) <obj.component /> 将被转换为 React.createElement(obj.component)<Component /> 将被转换为 React.createElement(Component)

为什么组件的构造函数只被调用一次?

React 协调算法假设如果自定义组件出现在后续渲染的相同位置,则它与之前的组件相同,因此重用前一个实例而不是创建新实例。

如何选择哪种方式创建组件

由于 React 团队已经声明 React.createClass 最终会被 React.Component 的类形式所取代。但是在找到 Mixins 替代方案之前是不会废弃掉 React.createClass 形式。所以:

能用 React.Component 创建的组件的就尽量不用 React.createClass 形式创建组件

除此之外,创建组件的形式选择还应该根据下面来决定:

1、只要有可能,尽量使用无状态组件创建形式。

2、否则(如需要 state、生命周期方法等),使用React.Component这种 es6 形式创建组件

react 如何区别 component 和 dom

当用 ReactDOM.render 创造一个 dom tree 的时候,一般有三种方式: (1) 第一个参数传 JSX 语法糖

ReactDOM.render(
  <button color="blue">OK</button>,
  document.getElementById("root")
);

React.createElement 会一个虚拟 dom 元素。

ReactDOM.render(
React.createElement({
type:'button',
props:{
color:'blue',
children:'OK!'
}
}),
document.getElementById('root')
);

虚拟 dom 是一个 obj:具有一个 type 属性代表当前的节点类型,还有节点的属性 props (2) 函数声明

function RenderButton() {
  return <button color="blue">OK</button>;
}
ReactDOM.render(<RenderButton />, document.getElementById("root"));

类声明

class DangerButton extends Component {
  render() {
    return <button color="blue">NOT OK</button>;
  }
}
ReactDOM.render(<DangerButton />, document.getElementById("root"));

如果我们组合三种方法创建一个节点:

const DeleteAccount = () => {
  <div>
    <p>Are you sure?</p>
    <DangerButton>Yep</DangerButton>
    <button color="blue">Cancel</button>
  </div>;
};

React.createElement 会把这个 JSX 转换为如下的虚拟 DOM:

const DeleteAccount = () => ({
  type: "div",
  props: {
    children: [
      {
        type: "p",
        props: {
          children: "Are you sure?",
        },
      },
      {
        type: "DangerButton",
        props: {
          children: "Yep",
        },
      },
      {
        type: "button",
        props: {
          color: "blue",
          children: "Cancel",
        },
      },
    ],
  },
});

当 React 碰到 type 是 function|class 时,它就知道这是个组件了

react 组件的通信

父子组件通信 传递 props 使用 context 子组件向父组件通信 利用回调函数 利用自定义事件机制 平级组件通信 各种数据流管理工具

a 组件在 b 组件内,c 组件在 a 组件内,如何让他渲染出来,a 组件和 c 组件同级

实现组件有哪些方式?

React 推出后,出于不同的原因先后出现三种定义 react 组件的方式,殊途同归;具体的三种方式:

1.函数式定义的无状态组件

2.es5 原生方式 React.createClass 定义的组件

3.es6 形式的 extends React.Component 定义的组件

虽然有三种方式可以定义 react 的组件,那么这三种定义组件方式有什么不同呢?或者说为什么会出现对应的定义方式呢?下面就简单介绍一下。

组件不会被实例化,整体渲染性能得到提升

因为组件被精简成一个 render 方法的函数来实现的,由于是无状态组件,所以无状态组件就不会在有组件实例化的过程,无实例化过程也就不需要分配多余的内存,从而性能得到一定的提升。

组件不能访问 this 对象

无状态组件由于没有实例化过程,所以无法访问组件 this 中的对象,例如:this.ref、this.state 等均不能访问。若想访问就不能使用这种形式来创建组件

源码阅读

https://github.com/BetaSu/just-react

Class Component

如何在没有 ES6 的情况下创建 React 类组件

如果你不使用 ES6,那么你可能需要使用 create-react-class 模块。对于默认属性,你需要在传递对象上定义 getDefaultProps() 函数。而对于初始状态,必须提供返回初始状态的单独 getInitialState 方法。

var Greeting = createReactClass({
  getDefaultProps: function () {
    return {
      name: "Jhohn",
    };
  },
  getInitialState: function () {
    return { message: this.props.message };
  },
  handleClick: function () {
    console.log(this.state.message);
  },
  render: function () {
    return <h1>Hello, {this.props.name}</h1>;
  },
});

注意: 如果使用 createReactClass,则所有方法都会自动绑定。也就是说,你不需要在事件处理程序的构造函数中使用 .bind(this)。

在 React 中如何使用装饰器?

你可以装饰你的类组件,这与将组件传递到函数中是一样的。 装饰器是修改组件功能灵活且易读的方式。

@setTitle("Profile")
class Profile extends React.Component {
  //....
}

/*
  title is a string that will be set as a document title
  WrappedComponent is what our decorator will receive when
  put directly above a component class as seen in the example above
*/
const setTitle = (title) => (WrappedComponent) => {
  return class extends React.Component {
    componentDidMount() {
      document.title = title;
    }

    render() {
      return <WrappedComponent {...this.props} />;
    }
  };
};

React.createClass

`React.createClass`是react刚开始推荐的创建组件的方式,这是ES5的原生的JavaScript来实现的React组件,其形式如下:
var InputControlES5 = React.createClass({
propTypes: {//定义传入props中的属性各种类型
    initialValue: React.PropTypes.string
},
defaultProps: { //组件默认的props对象
    initialValue: ''
},
// 设置 initial state
getInitialState: function() {//组件相关的状态对象
    return {
        text: this.props.initialValue || 'placeholder'
    };
},
handleChange: function(event) {
    this.setState({ //this represents react component instance
        text: event.target.value
    });
},
render: function() {
    return (
        <div>
            Type something:
            <input onChange={this.handleChange} value={this.state.text} />
        </div>
    );
}
});
InputControlES6.propTypes = {
initialValue: React.PropTypes.string
};
InputControlES6.defaultProps = {
initialValue: ''
};

与无状态组件相比,React.createClass 和后面要描述的 React.Component 都是创建有状态的组件,这些组件是要被实例化的,并且可以访问组件的生命周期方法。但是随着 React 的发展,React.createClass 形式自身的问题暴露出来:

React.createClass 会自绑定函数方法(不像 React.Component 只绑定需要关心的函数)导致不必要的性能开销,增加代码过时的可能性。 React.createClass 的 mixins 不够自然、直观;React.Component 形式非常适合高阶组件(Higher Order Components–HOC),它以更直观的形式展示了比 mixins 更强大的功能,并且 HOC 是纯净的 JavaScript,不用担心他们会被废弃。HOC 可以参考无状态组件(Stateless Component) 与高阶组件。

为什么使用 jsx 的组件中没有看到使用 react 却需要引入 react?

本质上来说 JSX 是 React.createElement(component, props, ...children)方法的语法糖。在 React 17 之前,如果使用了 JSX,其实就是在使用 React, babel 会把组件转换为 CreateElement 形式。在 React 17 之后,就不再需要引入,因为 babel 已经可以帮我们自动引入 react。

react-router

基本原理:

URL 对应 Location 对象,而 UI 是由 react 的 components 来决定的,这样就转变成 location 与 components 之间的同步问题

Hash 模式

www.test.com/#/ 就是 Hash URL,当 # 后面的哈希值发生变化时,可以通过 hashchange 事件来监听到 URL 的变化,从而进行跳转页面,并且无论哈希值如何变化,服务端接收到的 URL 请求永远是 www.test.com。

window.addEventListener("hashchange", () => {
  // ... 具体逻辑
});

History 模式

History 模式是 HTML5 新推出的功能,主要使用 history.pushStatehistory.replaceState 改变 URL。

通过 History 模式改变 URL 同样不会引起页面的刷新,只会更新浏览器的历史记录。

// 新增历史记录
history.pushState(stateObject, title, URL);
// 替换当前历史记录
history.replaceState(stateObject, title, URL);

当用户做出浏览器动作时,比如点击后退按钮时会触发 popState 事件

window.addEventListener("popstate", (e) => {
  // e.state 就是 pushState(stateObject) 中的 stateObject
  console.log(e.state);
});
  • 老浏览器的 history: 主要通过 hash 来实现,对应 createHashHistory
  • 高版本浏览器: 通过 html5 里面的 history,对应 createBrowserHistory
  • node 环境下: 主要存储在内存里面,对应 createMemoryHistory

伪代码实现

// createBrowserHistory(HTML5)中的前进实现
function finishTransition(location) {
  // ...
  const historyState = { key };
  // ...
  if (location.action === "PUSH") {
    window.history.pushState(historyState, null, path);
  } else {
    window.history.replaceState(historyState, null, path);
  }
}
// createHashHistory的内部实现
function finishTransition(location) {
  // ...
  if (location.action === "PUSH") {
    window.location.hash = path;
  } else {
    window.location.replace(
      window.location.pathname + window.location.search + "#" + path
    );
  }
}
// createMemoryHistory的内部实现
const entries = [];
function finishTransition(location) {
  // ...
  switch (location.action) {
    case "PUSH":
      entries.push(location);
      break;
    case "REPLACE":
      entries[current] = location;
      break;
  }
}

两种模式对比

  • Hash 模式只可以更改 # 后面的内容,History 模式可以通过 API 设置任意的同源 URL
  • History 模式可以通过 API 添加任意类型的数据到历史记录中,Hash 模式只能更改哈希值,也就是字符串
  • Hash 模式无需后端配置,并且兼容性好。History 模式在用户手动输入地址或者刷新页面的时候会发起 URL 请求,后端需要配置 index.html 页面用于匹配不到静态资源的时候

React Ref

在 React 中,refs 的作用是什么

Refs 可以用于获取一个 DOM 节点或者 React 组件的引用。应该避免使用 String 类型的 Refs 和内联的 ref 回调。Refs 回调是 React 所推荐的。

需要注意的是,由于 this.refs.[refName] 属性获取的是真实 DOM ,所以必须等到虚拟 DOM 插入文档以后,才能使用这个属性,否则会报错。

如何创建 refs?

refs 是使用 React.createRef() 方法创建的,并通过 ref 属性添加到 React 元素上。为了在整个组件中使用refs,只需将 ref 分配给构造函数中的实例属性。

class MyComponent extends React.Component {
  constructor(props) {
    super(props);
    this.myRef = React.createRef();
  }
  render() {
    return <div ref={this.myRef} />;
  }
}

为什么 String Refs 被弃用?

如果你以前使用过 React,你可能会熟悉旧的 API,其中的 ref 属性是字符串,如 ref={'textInput'},并且 DOM 节点的访问方式为this.refs.textInput。我们建议不要这样做,因为字符串引用有以下问题,并且被认为是遗留问题。字符串 refs 在 React v16 版本中被移除。

TODO: setState

setState 的调用并不会马上引起 state 的改变。因为 setState 是个异步 API,只有同步代码运行完毕才会执行。

异步的原因是setState 可能会导致 DOM 的重绘,如果调用一次就马上去进行重绘,那么调用多次就会造成不必要的性能损失。设计成异步的话,就可以将多次调用放入一个队列中,在恰当的时候统一进行更新过程。

如果需要对 state 数据更改监听,setState 提供第二个参数,就是用来监听 state 里面数据的更改,当数据更改完成,调用回调函数。

如果直接更新状态, 那么组件将不会重新渲染。

为什么我们需要将函数传递给 setState() 方法?

这背后的原因是 setState() 是一个异步操作。出于性能原因,React 会对状态更改进行批处理,因此在调用 setState() 方法之后,状态可能不会立即更改。

为什么函数比对象更适合于 setState()?

出于性能考虑,React 可能将多个 setState() 调用合并成单个更新。这是因为我们可以异步更新 this.propsthis.state,所以不应该依赖它们的值来计算下一个状态。

以下的 counter 示例将无法按预期更新:

// Wrong
this.setState({
  counter: this.state.counter + this.props.increment,
});

首选方法是使用函数而不是对象调用 setState()。该函数将前一个状态作为第一个参数,当前时刻的 props 作为第二个参数。

// Correct
this.setState((prevState, props) => ({
  counter: prevState.counter + props.increment,
}));

setState 循环调用风险

如果在shouldComponentUpdatecomponentWillUpdate 方法里调用 this.setState 方法,就会造成崩溃。

setState 步骤

  1. 将传递给 setState 的对象合并到组件的当前状态,触发所谓的调和过程 (Reconciliation)
  2. 然后生成新的 DOM 树并和旧的 DOM 树使用 Diff 算法对比
  3. 根据对比差异对界面进行最小化重渲染

setState()replaceState() 方法之间有什么区别?

当你使用 setState() 时,当前和先前的状态将被合并。replaceState() 会抛出当前状态,并仅用你提供的内容替换它。通常使用 setState(),除非你出于某种原因确实需要删除所有以前的键。你还可以在 setState() 中将状态设置为 false/null,而不是使用 replaceState()

是否可以在不调用 setState 方法的情况下,强制组件重新渲染?

默认情况下,当组件的状态或属性改变时,组件将重新渲染。如果你的 render() 方法依赖于其他数据,你可以通过调用 forceUpdate() 来告诉 React,当前组件需要重新渲染。

component.forceUpdate(callback);

建议避免使用 forceUpdate(),并且只在 render() 方法中读取 this.propsthis.state

React setState 笔试题,下面的代码输出什么?

class Example extends React.Component {
  constructor() {
    super();
    this.state = {
      val: 0,
    };
  }

  componentDidMount() {
    this.setState({ val: this.state.val + 1 });
    console.log(this.state.val); // 第 1 次 log

    this.setState({ val: this.state.val + 1 });
    console.log(this.state.val); // 第 2 次 log

    setTimeout(() => {
      this.setState({ val: this.state.val + 1 });
      console.log(this.state.val); // 第 3 次 log

      this.setState({ val: this.state.val + 1 });
      console.log(this.state.val); // 第 4 次 log
    }, 0);
  }

  render() {
    return null;
  }
}

0 0 2 3

  1. 第一次和第二次都是在 react 自身生命周期内,触发时 isBatchingUpdates 为 true,所以并不会直接执行更新 state,而是加入了 dirtyComponents,所以打印时获取的都是更新前的状态 0。

  2. 两次 setState 时,获取到 this.state.val 都是 0,所以执行时都是将 0 设置成 1,在 react 内部会被合并掉,只执行一次。设置完成后 state.val 值为 1。

  3. setTimeout 中的代码,触发时 isBatchingUpdates 为 false,所以能够直接进行更新,所以连着输出 2,3。

React 中 setState 什么时候是同步的,什么时候是异步的?

在 React 中,如果是由 React 引发的事件处理(比如通过 onClick 引发的事件处理),调用 setState 不会同步更新 this.state,除此之外的 setState 调用会同步执行 this.state。所谓“除此之外”,指的是绕过 React 通过 addEventListener 直接添加的事件处理函数,还有通过 setTimeout/setInterval 产生的异步调用。

简单说就是只在合成事件和钩子函数中是“异步”的,在原生事件和 setTimeout 中都是同步的。

**原因:**在 React 的 setState 函数实现中,会根据一个变量 isBatchingUpdates 判断是直接更新 this.state 还是放到队列中回头再说,而 isBatchingUpdates 默认是 false,也就表示 setState 会同步更新 this.state,但是,有一个函数 batchedUpdates,这个函数会把 isBatchingUpdates 修改为 true,而当 React 在调用事件处理函数之前就会调用这个 batchedUpdates,造成的后果,就是由 React 控制的事件处理过程 setState 不会同步更新 this.state。

异步更新

考虑到性能问题,setState 使用一个队列机制来更新 state。 当执行 setState 时,会将需要更新的 state浅合并后放入状态队列,不会立即更新 state。而如果不使用 setState,而直接修改 state 的值就不会放入状态队列,下一次调用 setState 对状态队列进行更新的时候可能会造成不可预知的错误。

例子:

// 假设 state.count === 0
this.setState({ count: state.count + 1 });
this.setState({ count: state.count + 1 });
this.setState({ count: state.count + 1 });
// state.count === 1, 而不是 3

本质上等同于:

// 假设 state.count === 0
Object.assign(
  state,
  { count: state.count + 1 },
  { count: state.count + 1 },
  { count: state.count + 1 }
);
// {count: 1}

解决方法为: 传递一个签名为 (state, props) => newState 的函数作为参数。 向 setState 中传入函数时,这个函数不会被浅合并,一定会执行,是一个原子性更新操作。

// 正确用法
this.setState((prevState, props) => ({
  count: prevState.count + props.increment,
}));

Context 的 Provider 和 Consumer

什么是 React Context(Context)?

Context 通过组件树提供了一个传递数据的方法,从而避免了在每一个层级手动的传递 props。比如,需要在应用中许多组件需要访问登录用户信息、地区偏好、UI 主题等。

// 创建一个 theme Context,  默认 theme 的值为 light
const ThemeContext = React.createContext("light");

function ThemedButton(props) {
  // ThemedButton 组件从 context 接收 theme
  return (
    <ThemeContext.Consumer>
      {(theme) => <Button {...props} theme={theme} />}
    </ThemeContext.Consumer>
  );
}

// 中间组件
function Toolbar(props) {
  return <ThemedButton />;
}

class App extends React.Component {
  render() {
    return (
      <ThemeContext.Provider value="dark">
        <Toolbar />
      </ThemeContext.Provider>
    );
  }
}

什么是 Consumer?

Consumer 是一个订阅 Context 更改的 React 组件。它需要一个函数作为子元素,该函数接收当前上下文的值作为参数,并返回一个 React 元素。传递给函数 value 参数的参数值将等于在组件树中当前组件最近的 Provider 元素的 value 属性值。

<MyContext.Consumer>
  {value => /* render something based on the context value */}
</MyContext.Consumer>

在 context 中默认值的目的是什么?

当在组件树中的组件没有匹配到在其上方的 Provider 时,才会使用 defaultValue 参数。这有助于在不包装组件的情况下单独测试组件。下面的代码段提供了默认的主题值 Luna。

const defaultTheme = "Luna";
const MyContext = React.createContext(defaultTheme);

React 渲染

当组件重新渲染时顺序执行的方法有哪些?

更新可能由属性或状态的更改引起。在重新渲染组件时,会按以下顺序调用下列方法。

  • static getDerivedStateFromProps()
  • shouldComponentUpdate()
  • render()
  • getSnapshotBeforeUpdate()
  • componentDidUpdate()

React 的批量更新机制 BatchUpdates?

从源码全面剖析 React 组件更新机制open in new window

forceUpdate 经历了哪些生命周期,子组件呢?

forceUpdate 将会经历:

  • componentWillUpdate
  • render
  • componentDidUpdate 调用 forceUpdate() 将会导致 render() 方法在相应的组件上被调用,并且子级组件也会调用自己的 render(),但是如果标记改变了,那么 React 仅会更新 DOM。

通常情况下,应该尽量避免所有使用 forceUpdate() 的情况,在 render() 中仅从 this.props 和 this.state 中读取数据。这会使应用大大简化,并且更加高效

在 React 测试中什么是浅层渲染(Shallow Renderer)?

浅层渲染对于在 React 中编写单元测试用例很有用。它允许您渲染一个一级深的组件并断言其渲染方法返回的内容,而不必担心子组件未实例化或渲染。

例如,如果您有以下组件:

function MyComponent() {
  return (
    <div>
      <span className={"heading"}>{"Title"}</span>
      <span className={"description"}>{"Description"}</span>
    </div>
  );
}

然后你可以如下断言:

import ShallowRenderer from "react-test-renderer/shallow";

// in your test
const renderer = new ShallowRenderer();
renderer.render(<MyComponent />);

const result = renderer.getRenderOutput();

expect(result.type).toBe("div");
expect(result.props.children).toEqual([
  <span className={"heading"}>{"Title"}</span>,
  <span className={"description"}>{"Description"}</span>,
]);

React 中的样式问题

在 React 中如何使用 innerHTML?

dangerouslySetInnerHTML 属性是 React 用来替代在浏览器 DOM 中使用 innerHTML。与 innerHTML 一样,考虑到跨站脚本攻击(XSS),使用此属性也是有风险的。使用时,你只需传递以 __html 作为键,而 HTML 文本作为对应值的对象。

在本示例中 MyComponent 组件使用 dangerouslySetInnerHTML 属性来设置 HTML 标记:

function createMarkup() {
  return { __html: "First &middot; Second" };
}

function MyComponent() {
  return <div dangerouslySetInnerHTML={createMarkup()} />;
}

如何在 React 中使用样式?

style 属性接受含有 camelCased(驼峰)属性的 JavaScript 对象,而不是 CSS 字符串。这与 DOM 样式中的 JavaScript 属性一致,效率更高,并且可以防止 XSS 安全漏洞。

const divStyle = {
  color: "blue",
  backgroundImage: "url(" + imgUrl + ")",
};

function HelloWorldComponent() {
  return <div style={divStyle}>Hello World!</div>;
}

为了与在 JavaScript 中访问 DOM 节点上的属性保持一致,样式键采用了 camel-cased(例如node.style.backgroundImage)。

react 中的几种 css 形式

  1. import css 这个实际使用跟 link css 差不多,不需要再手动为 className 赋值
  2. css module const style = require('./style.css') 每一个 className 都需要手动赋值
  3. styled-components. 在 js 中编写 CSS

Styled Components

import React from "react";
import styled from "styled-components";

const Title = styled.h1`
  font-size: 1.5em;
  text-align: center;
  color: palevioletred;
`;

const Wrapper = styled.section`
  padding: 4em;
  background: papayawhip;
`;

TitleWrapper变量现在是可以像任何其他 react 组件一样渲染。

<Wrapper>
  <Title>{"Lets start first styled component!"}</Title>
</Wrapper>
Last Updated:
Contributors: yiliang114